package com.lili.collection.core.processor;

import com.lili.collection.core.constans.LevelConstants;
import com.lili.collection.core.enums.FieldType;
import com.lili.collection.core.utils.JsonPathAnalysisUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.Data;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 *描述: 生成新JSON结构体的去扁平化
 *@date: 2023/10/25  16:57
 *@author: lili
 */
public class JSONUnFlatProcessor {

    private JSONObject unFlatJsonObj;

    public JSONUnFlatProcessor(){
        this.unFlatJsonObj=new JSONObject();
    }

    public JSONObject getRootJSONObj(){
        return this.unFlatJsonObj;
    }

    public void parseSingleMap(Map<String,Object> dataMap){
        if(MapUtils.isEmpty(dataMap)){
            return;
        }
        Map<String,JSONObject> levelObjMap=new HashMap<>();
        Map<String,JSONArray> levelArrayMap=new HashMap<>();
        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if(StringUtils.isBlank(key)){
                continue;
            }
            String parentPath = JsonPathAnalysisUtil.getParentPath(key);
            Object cacheObj = createCacheObj(parentPath, levelObjMap, levelArrayMap);
            String filedName = JsonPathAnalysisUtil.getPathField(key);
            if(cacheObj!=null){
                if(cacheObj instanceof JSONArray){
                    if(value!=null){
                        ((JSONArray)cacheObj).add(value);
                    }
                }else if(cacheObj instanceof JSONObject){
                    ((JSONObject)cacheObj).put(filedName,value);
                }
            }else {
                createCollectLevel(levelObjMap,levelArrayMap, key, value, filedName);
            }
        }
    }

    public Object parseOneRow(String parentPath,Map<String,JSONObject> levelObjMap){
        if(StringUtils.isBlank(parentPath)){
            return null;
        }
        return createCollectLevelWithObj(levelObjMap, null, parentPath);
    }

    private Object createCacheObj(String parentPath,Map<String,JSONObject> levelObjMap,Map<String,JSONArray> levelArrayMap){
        if(levelObjMap.containsKey(parentPath)){
            return levelObjMap.get(parentPath);
        }
        if(levelArrayMap.containsKey(parentPath)){
            return levelArrayMap.get(parentPath);
        }
        if(parentPath.endsWith(LevelConstants.ARRAY) || parentPath.endsWith(LevelConstants.ARRAY_BASE)){
            return null;
        }
        int length = JsonPathAnalysisUtil.getLevelNumber(parentPath);
        for (int i = length; i >= 1; i--) {
            //选择的路径
            String choosePath = JsonPathAnalysisUtil.choosePathByNum(parentPath,i);
            if(levelObjMap.containsKey(choosePath)){
                JSONObject jsonObject = levelObjMap.get(choosePath);
                String suffixPath = JsonPathAnalysisUtil.getSuffixPath(parentPath, choosePath);
                JSONUnFlatProcessor processor=new JSONUnFlatProcessor();
                Map<String,JSONObject> newlevelObjMap=new HashMap<>();
                Object parseObj = processor.parseOneRow(suffixPath, newlevelObjMap);
                JSONObject rootJSONObj = processor.getRootJSONObj();
                //进行合并
                if(MapUtils.isNotEmpty(rootJSONObj)){
                    jsonObject.putAll(rootJSONObj);
                }
                if(MapUtils.isNotEmpty(newlevelObjMap)){
                    //加入前缀
                    for (Map.Entry<String, JSONObject> entry : newlevelObjMap.entrySet()) {
                        String realKey=choosePath+"."+entry.getKey();
                        levelObjMap.put(realKey,entry.getValue());
                    }
                }
                return parseObj;
            }
            if(levelArrayMap.containsKey(choosePath)){
                return levelArrayMap.get(choosePath);
            }
            //单key
            String singleKey = JsonPathAnalysisUtil.getPathKeyByNum(parentPath, i);
            FieldType fieldType = JsonPathAnalysisUtil.getFieldType(singleKey);
            if(fieldType == FieldType.ARRAY || fieldType==FieldType.ARRAY_BASE){
                return null;
            }
        }
        return null;
    }

    private void createCollectLevel(Map<String, JSONObject> levelObjMap,Map<String,JSONArray> levelArrayMap, String key, Object value, String filedName) {
        CollectLevelDO collect = getJSONObjByPath(key);
        JSONArray pathArray = collect.getPathArray();
        if(pathArray!=null){
            if(value!=null){
                pathArray.add(value);
            }
        }else {
            collect.getPathObj().put(filedName, value);
        }
        if(MapUtils.isNotEmpty(collect.getLevelObjMap())){
            levelObjMap.putAll(collect.getLevelObjMap());
        }
        if(MapUtils.isNotEmpty(collect.getLevelArray())){
            levelArrayMap.putAll(collect.getLevelArray());
        }
    }

    private Object createCollectLevelWithObj(Map<String, JSONObject> levelObjMap,Map<String,JSONArray> levelArrayMap, String key) {
        CollectLevelDO collect = getJSONObjByPath(key);
        JSONArray pathArray = collect.getPathArray();
        if(pathArray!=null){
            if(MapUtils.isNotEmpty(collect.getLevelArray())){
                levelArrayMap.putAll(collect.getLevelArray());
            }
            return pathArray;
        }else {
            if(MapUtils.isNotEmpty(collect.getLevelObjMap())){
                levelObjMap.putAll(collect.getLevelObjMap());
            }
            return collect.getPathObj();
        }
    }

    public void parseArrayData(String data){
        if(StringUtils.isBlank(data)){
            return;
        }
        JSONArray array = JSON.parseArray(data);
        for (Object obj : array) {
            if(obj instanceof JSONObject){
                parseSingleMap((JSONObject)obj);
            }
        }
    }

    @Override
    public String toString() {
        ignoreEmptyObject(unFlatJsonObj);
        return JSON.toJSONString(unFlatJsonObj, JSONWriter.Feature.WriteMapNullValue);
    }

    private void ignoreEmptyObject(Object jsonObj) {
        if (jsonObj == null) {
            return;
        }
        if (jsonObj instanceof JSONArray) {
            JSONArray array = (JSONArray) jsonObj;
            Iterator<Object> iterator = array.iterator();
            while(iterator.hasNext()){
                Object next = iterator.next();
                if(next instanceof JSONObject && needIgnore((JSONObject)next)){
                    iterator.remove();
                }else if(next != null && ((next instanceof JSONObject) || (next instanceof JSONArray))) {
                    ignoreEmptyObject(next);
                }
            }
        } else {
            //行数据
            JSONObject jsonObject = (JSONObject) jsonObj;
            for (String key : jsonObject.keySet()) {
                Object objValue = jsonObject.get(key);
                if (objValue != null && ((objValue instanceof JSONObject) || (objValue instanceof JSONArray))) {
                    ignoreEmptyObject(objValue);
                }
            }
        }
    }

    private boolean needIgnore(JSONObject jsonObj){
        if(MapUtils.isEmpty(jsonObj)){
            return true;
        }
        boolean hasValue=false;
        for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
            if(entry.getValue()!=null){
                hasValue=true;
            }
        }
        return !hasValue;
    }

    private CollectLevelDO getJSONObjByPath(String pathKey){
        int number = JsonPathAnalysisUtil.getLevelNumber(pathKey);
        if(number<=0){
            return null;
        }
        CollectLevelDO levelDO=new CollectLevelDO();
        Map<String,JSONObject> levelMap=new HashMap<>();
        Map<String,JSONArray> levelArray=new HashMap<>();
        levelDO.setLevelObjMap(levelMap);
        levelDO.setLevelArray(levelArray);
        JSONObject currentJSONObj=null;
        for (int i = 1; i <= number; i++) {
            String singleKey = JsonPathAnalysisUtil.getPathKeyByNum(pathKey, i);
            if(StringUtils.isBlank(singleKey)){
                break;
            }
            FieldType fieldType = JsonPathAnalysisUtil.getFieldType(singleKey);
            if(fieldType==null || fieldType == FieldType.BASE){
                if(i==1 && fieldType == FieldType.BASE){
                    currentJSONObj=unFlatJsonObj;
                }
                break;
            }
            String realKey = JsonPathAnalysisUtil.getRealKey(singleKey);
            if(fieldType==FieldType.OBJECT){
                if(i==1){
                    currentJSONObj = getJsonObject(unFlatJsonObj,realKey);
                }else {
                    currentJSONObj = getJsonObject(currentJSONObj,realKey);
                }
            }else if(fieldType==FieldType.ARRAY){
                if(i==1){
                    currentJSONObj = getCurrentJSONObj(unFlatJsonObj, realKey);
                }else {
                    currentJSONObj = getCurrentJSONObj(currentJSONObj, realKey);
                }
            }else if(fieldType==FieldType.ARRAY_BASE){
                if(i==1){
                    JSONArray currentArray = getCurrentArray(unFlatJsonObj, realKey);
                    levelArray.put(JsonPathAnalysisUtil.choosePathByNum(pathKey,i),currentArray);
                    levelDO.setPathArray(currentArray);
                    return levelDO;
                }else {
                    JSONArray currentArray = getCurrentArray(currentJSONObj, realKey);
                    levelArray.put(JsonPathAnalysisUtil.choosePathByNum(pathKey,i),currentArray);
                    levelDO.setPathArray(currentArray);
                    return levelDO;
                }
            }
            levelMap.put(JsonPathAnalysisUtil.choosePathByNum(pathKey,i),currentJSONObj);
        }
        levelDO.setPathObj(currentJSONObj);
        return levelDO;
    }

    private JSONArray getCurrentArray(JSONObject currentJSONObj,String realKey){
        Object obj = currentJSONObj.get(realKey);
        if(obj!=null && obj instanceof JSONArray){
            JSONArray array=(JSONArray)obj;
            return array;
        } else {
            JSONArray array=new JSONArray();
            currentJSONObj.put(realKey,array);
            return array;
        }
    }

    private JSONObject getCurrentJSONObj(JSONObject currentJSONObj, String realKey) {
        JSONObject resultJSONObject=null;
        Object obj = currentJSONObj.get(realKey);
        if(obj!=null){
            if(obj instanceof JSONArray){
                JSONArray array=(JSONArray)obj;
                JSONObject jsonObject=new JSONObject();
                array.add(jsonObject);
                currentJSONObj.put(realKey,array);
                resultJSONObject =jsonObject;
            }
        } else {
            JSONArray array=new JSONArray();
            JSONObject jsonObject=new JSONObject();
            array.add(jsonObject);
            currentJSONObj.put(realKey,array);
            resultJSONObject =jsonObject;
        }
        return resultJSONObject;
    }

    private JSONObject getJsonObject(JSONObject obj,String realKey) {
        JSONObject currentJSONObj;
        Object keyObj = obj.get(realKey);
        if(keyObj!=null){
            currentJSONObj=(JSONObject)keyObj;
        }else {
            JSONObject jsonObject=new JSONObject();
            obj.put(realKey,jsonObject);
            currentJSONObj=jsonObject;
        }
        return currentJSONObj;
    }

    @Data
    private class CollectLevelDO {

        private JSONObject pathObj;

        private Map<String,JSONObject> levelObjMap;

        private Map<String,JSONArray> levelArray;

        private JSONArray pathArray;
    }
}